home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
SGI Hot Mix 17
/
Hot Mix 17.iso
/
HM17_SGI
/
research
/
examples
/
demo
/
demosrc
/
d_uscensus.pro
< prev
next >
Wrap
Text File
|
1997-07-08
|
44KB
|
1,387 lines
Function USCensus$ZColors, PopulationChanges, PopulationScale
;
; Given a population change, determine the color that should be
; used for that State.
;
NPopulations = N_Elements(PopulationChanges)
Colors = BytArr(3, NPopulations)
Loss = Where(PopulationChanges lt PopulationScale.RangeMax[0], NLoss)
If (NLoss gt 0) then Begin
For J = 0, 2 Do Begin
Colors[J, Loss] = PopulationScale.Colors[J, 0]
EndFor
EndIf
For I = 1, N_elements(PopulationScale.RangeMax) - 1 Do Begin
ThisColor = Where(PopulationChanges ge $
PopulationScale.RangeMax[I - 1] and $
PopulationChanges lt PopulationScale.RangeMax[I], NThisColor)
If (NThisColor ne 0) then Begin
For J = 0, 2 Do Begin
Colors[J, ThisColor] = PopulationScale.Colors[J, I]
EndFor
EndIf
EndFor
BigGain = Where(PopulationChanges ge PopulationScale.RangeMax[ $
N_elements(PopulationScale.RangeMax) - 1], NBigGain)
If (NBigGain ne 0) then Begin
For J = 0, 2 Do Begin
Colors[J, BigGain] = PopulationScale.Colors[J, $
N_elements(PopulationScale.RangeMax) - 1]
EndFor
EndIf
Return, Colors
End
Pro USCensus$New_Coordinates, States, XSize, YSize
;
; Calculate State outlines and extruded skirts in normalized
; coordinates based on the current window size. This routine
; is called on initialization.
;
;
; Save the relevant system variables.
;
PSave = !p
XSave = !x
YSave = !y
ZSave = !z
;
; Set up mapping so we can perform coordinate transforms
; from lat/long to normal coordinates.
;
Window, /Free, /Pixmap, XSize = XSize, YSize = YSize
Map_Set, 30., -90., Limit = [10.,-68.5, 60.,-120.5], /Lambert
WDelete, !d.window
;
; Loop over States, converting lat/lon coordinates to normal
; coordinates, and extruding the outline along the Z axis to
; build the "sides" of the States. The Z height is random
; in this example, though we do have population data in the
; State structure.
;
NPopulation = N_elements(States[0].Population) - 1
For I = 0L, N_elements(States) - 1 Do Begin
NewOutline = (Convert_Coord(*States[I].pOutline, /Data, $
/To_Normal))
NSize = N_elements(NewOutline)/3
States[I].ZValue = 1.
If (Ptr_Valid(States[I].pNormalOutline)) then Begin
*States[I].pNormalOutline = NewOutline[*, 0:NSize - 1]
EndIf Else Begin
States[I].pNormalOutline = $
Ptr_New(NewOutline[*, 0:NSize - 1])
EndElse
;
; Meshing needs to have the first and last outline elements the same
; otherwise the surface doesn't close.
;
NewOutline2 = FltArr(3, NSize + 1)
NewOutline2[*, 0:NSize - 1] = NewOutline
NewOutline2[*, NSize] = NewOutline[*, 0]
Mesh_Obj, 5, VertexList, PolygonList, NewOutline2, P1 = 1, $
P2 = [0, 0, States[I].ZValue]
If (Ptr_Valid(States[I].pPolygonList)) then Begin
*States[I].pPolygonList = Temporary(PolygonList)
EndIf Else Begin
States[I].pPolygonList = Ptr_New(PolygonList, /No_Copy)
EndElse
If (Ptr_Valid(States[I].pVertexList)) then Begin
*States[I].pVertexList = Temporary(VertexList)
EndIf Else Begin
States[I].pVertexList = Ptr_New(VertexList, /No_Copy)
EndElse
EndFor
;
; Restore plotting environment that changed with MAP_SET.
;
!x = XSave
!y = YSave
!z = ZSave
!p = PSave
Return
End
Pro USCensus$Build_Backdrop, BackdropImage, BackdropObject
;
; This routine builds the "curtain" or backdrop
; in front of which the map data are rendered.
; There's no real magic here. I just played around
; with the values until I got the effect I wanted.
;
; Note that when texture mapping, IDL operates most
; efficiently on images that are a) square and b)
; dimensioned by powers of 2.
;
; ZZ defines the surface "height" of the backdrop.
; XX and YY are the coordinates of each of the
; ZZ values.
;
; Since the image is not a true child of the
; backdrop object (it's a property), we need
; to pass that back so we can destroy it later.
;
ZZ = Findgen(256)^7/1.E15 + .01
Z = FltArr(256, 256)
For I = 0, 255 Do Begin
Z[I, *] = ZZ
EndFor
XX = FltArr(256, 256)
YY = XX
;
; Center the X and Y values
;
X = Reverse(.25 - Findgen(256)/512)
Y = Findgen(256)/128 - 1
;
; The Y values of the mapped image are
; the same across a row. The X values
; spread out toward the bottom of the
; backdrop so the image appears "splayed".
;
For I = 0, 255 Do Begin
YY[I, *] = Y/10.
XX[*, I] = X * (1 + ((255 - I)/128.)^1.5)
EndFor
;
; Create the image that will be mapped onto
; the coordinates defined above, It's simply
; an image of vertical stripes with some border
; pixels.
;
Image = BytArr(3, 256, 256)
Image[*,254:*, *] = 255
Image[*, *, 254:*] = 255
Image[*,0:3, *] = 255
Image[*, *, 0:2] = 255
;
; Now we get obnoxiously partiotic and make
; the stripes red, white, and blue with some
; black left in for "shadows".
;
K = -1
For X = 30, 220, 10 Do Begin
K = (K + 1) mod 3
If (K eq 1) then Begin
Image[*, X:X + 5, *] = 255
EndIf Else Begin
For I = 0, 2 Do Begin
Image[I, X:X + 5, *] = 255 * (I eq K)
EndFor
EndElse
EndFor
;
; The next trick we perform is to de-focus the
; image in steps so that pixels nearest the
; top that appear farthest away are also the most
; out-of-focus.
;
For Y = (Size(Image))[2] - 31, 0, -32 Do Begin
For I = 0, 2 Do Begin
;
; Smooth pixels from this row up. Rows at the
; top are smoothed more often than rows at the
; bottom.
;
Layer = Smooth(Reform(Image[I, *, Y:*]), 3)
Image[I, *, Y:*] = Layer
EndFor
EndFor
;
; Create the backdrop image. Don't forget it's TrueColor!
;
BackdropImage = Obj_New('IDLgrImage', Image, Interleave = 0)
;
; Create the backdrop object using the image object as the
; texture object. Keep in mind that the texture mapped image
; colors are convolved with the COLOR property of the surface
; object.
;
BackdropObject = Obj_New('IDLgrSurface', .2 - Z/100., XX, YY, $
Shading = 1, Style = 2, Texture_Map = BackdropImage, $
Color = [255, 255, 255], /Texture_Interp)
End
Pro USCensus$Build_Scale_Legend, PopulationScale, TimesFont, $
ScaleModel
;
; This routine builds the legend for the display. Each key
; consists of a color block and a text label associated
; with each color. Colors in this example represent relative
; population change from one decade to the next.
;
ScaleModel = Obj_New('IDLgrModel')
NScaleColors = N_elements(PopulationScale.RangeMax) + 2
ScaleColorModels = ObjArr(NScaleColors)
ScaleColorImages = ObjArr(NScaleColors)
ScaleColorLabels = ObjArr(NScaleColors)
ColorBlock = BytArr(3, 30, 20)
;
; The color white is used when a State is "new"; no census
; was performed the previous decade.
;
ScaleColorImages[0] = Obj_New('IDLgrImage', ColorBlock + 255, $
Interleave = 0, Dimensions = [.1, .2])
ScaleColorLabels[0] = Obj_New('IDLgrText', 'New State', /OnGlass, $
Location = [.11, -0.06], Color = [255, 255, 255], $
Font = TimesFont)
;
; Red indicates there was population loss compared with the
; previous decade.
;
Red = ColorBlock
Red[0, *, *] = 255
ScaleColorImages[1] = Obj_New('IDLgrImage', Red, Interleave = 0, $
Dimensions = [.1, .2])
ScaleColorLabels[1] = Obj_New('IDLgrText', 'Pop. Loss', /OnGlass, $
Location = [.11, -0.06], Color = [255, 255, 255], $
Font = TimesFont)
;
; The remaining keys indicate population change between one
; percentage and the next.
;
For I = 2, NScaleColors - 1 Do Begin
ThisColor = ColorBlock
For J = 0, 2 Do Begin
ThisColor[J, *, *] = PopulationScale.Colors[J, I - 1]
EndFor
ScaleColorImages[I] = Obj_New('IDLgrImage', ThisColor, $
Interleave = 0, Dimensions = [0.1, 0.2])
ScaleColorLabels[I] = Obj_New('IDLgrText', '> ' + $
StrTrim(Fix(100*(PopulationScale.RangeMax[I - 2] - $
.99999)), 2) + '%', $
Color = [255, 255, 255], Location = [0.11, -0.06], $
/OnGlass, Font = TimesFont)
EndFor
;
; Combine the color blocks and text into a models, then
; translate them so we end up in 2 rows, each with 5 entries.
;
For I = 0, NScaleColors - 1 Do Begin
ScaleColorModels[I] = Obj_New('IDLgrModel')
ScaleColorModels[I]->Add, ScaleColorImages[I]
ScaleColorModels[I]->Add, ScaleColorLabels[I]
ScaleColorModels[I]->Translate, -.94 + .4*(I mod 5), $
-.575*(Fix(I/5)), 0.
EndFor
ScaleLabel = Obj_New('IDLgrText', 'Relative Population Change', $
Location = [-.98, .6], Font = TimesFont, /OnGlass, $
Color = [255, 255, 255])
ScaleModel->Add, ScaleLabel
ScaleModel->Add, ScaleColorModels
End
Pro USCensus$Build_State_Objects, States, StateSurfaceModel, $
StateObjects, StateSkirts, StateFaces, StateOutlines
;
; Create a tessellator object. Since the State outlines
; are not all convex, we need to tessellate them before
; turning them into polygon objects.
;
Tessellator = Obj_New('IDLgrTessellator')
;
; Build each of the State objects. State objects consist
; of a) a contiguous outline of the State, b) a filled
; polygon which serves as the face, and c) an extruded
; surface which is the "skirt" of the State.
;
NStates = N_elements(States)
StateSurfaceModel = Obj_New('IDLgrModel')
StateObjects = ObjArr(NStates)
StateSkirts = ObjArr(NStates)
StateFaces = ObjArr(NStates)
StateOutlines = ObjArr(NStates)
For I = 0, NStates - 1 Do Begin
NewOutline = *States[I].pNormalOutline
;
; Make sure lakes are blue. And that no one lives permanently
; in them. Note to Canadians: boundaries are cosmetic only.
; Neither the United States of America nor Research Systems, Inc.
; make any formal claims to territories displayed in the map
; data that do not actually represent previously agreed upon
; legal boundaries claimed by either entity.
;
NewOutline[2, *] = States[I].ZValue
If (StrPos(States[I].State, 'LAKE') ne -1) then Begin
Color = [0, 0, 255]
EndIf Else Begin
Color = [255, i*3, i*5]
EndElse
;
; Create the State model object.
;
StateObjects[I] = Obj_New('IDLgrModel')
;
; A black outline serves as the State border. We define
; this first in the model so it will overlay the polygon
; face defined later. (They lie in the same plane.)
;
StateOutlines[I] = Obj_New('IDLgrPolyline', NewOutline, $
Color=[0, 0, 0], Thick = 1.2, Shading = 0)
StateObjects[I]->Add, StateOutlines[I]
;
; Create the skirt for the State. Note that we specify
; backface culling. Since we won't be looking at the
; "inside" or back of the scene, we can speed rendering
; by telling IDL to reject back faces.
;
StateSkirts[I] = Obj_New('IDLgrPolygon',*States[I].pVertexList, $
Polygon = *States[I].pPolygonList, Color = Color, $
Shading = 1, Reject = 1)
StateObjects[I]->Add, StateSkirts[I]
;
; Create the top face of the State. We don't define
; the polygon outlines until a little later after
; we tessellate the polygon.
;
StateFaces[I] = Obj_New('IDLgrPolygon', $
Color = Color, Shading = 0, Reject = 1)
StateObjects[I]->Add, StateFaces[I]
;
; Set the UValue of the State object to the State's name.
;
StateObjects[I]->SetProperty, UValue = States[I].State
;
; The following tessellation code compensates for the "concave
; polygon problem". Tessellate the outline.
;
Tessellator->AddPolygon, NewOutline*500.
;
; Precalculate the normals; all the faces point straight up.
;
If (Tessellator->Tessellate(Vertices, Polygons)) then Begin
;
; Define the State face outline from the tesselation results.
; We precalculate the normals since we know the faces are all
; perpendicular to the +Z axis. This saves some calculation
; time for IDL.
;
Normals = Vertices
Normals[0:1, *] = 0.
Normals[2, *] = 1.
StateFaces[I]->SetProperty, Data = Vertices/500., $
Polygons = Polygons, Normals = Normals
EndIf
;
; Translate Alaska and Hawaii so they lie closer to the
; continental USA.
;
If (States[I].State eq 'ALASKA') then Begin
StateObjects[I]->Translate, 0., -.15, 0.
StateObjects[I]->Translate, -.05, 0., 0.
EndIf
If (States[I].State eq 'HAWAII') then Begin
StateObjects[I]->Translate, .125, 0., 0.
StateObjects[I]->Translate, 0., -.05, 0.
EndIf
;
; Add the State to the model. Also reset the
; tessellator so we're only working on one polygon
; at a time.
;
StateSurfaceModel->Add, StateObjects[I]
Tessellator->Reset
EndFor
;
; We don't need the tessellator anymore.
;
Obj_Destroy, Tessellator
End
Pro USCensus$Display_Census_Year, AppState
;
; Translate the surface back to the XY plane so we can
; rotate it about the Y axis.
;
AppState.StateSurfaceModel->Translate, 0., 0., -.5
;
; Rotate the plane about Y back to 0 degrees, then rotate
; it the specified number of degrees relative to 0.
;
AppState.StateSurfaceModel->GetProperty, Transform = Transform
ThetaY = ATan(Transform[2, 0], Transform[0, 0])/!dtor
AppState.StateSurfaceModel->Rotate, [0., 1., 0.], -ThetaY
AppState.StateSurfaceModel->Rotate, [0., 1., 0.], AppState.ThetaY
;
; Translate the surface back toward the viewer along Z.
;
AppState.StateSurfaceModel->Translate, 0., 0., .5
ZeroPops = Where(AppState.States.Population[ $
AppState.CurrentYearIndex] eq 0, NZeroPops)
OkayPops = Where(AppState.States.Population[ $
AppState.CurrentYearIndex] ne 0, NOKayPops)
;
; For States that had no census taken for a given decade,
; we just draw dark gray boxes at Z = very nearly zero.
; Lakes are always blue.
;
For I = 0, NZeroPops - 1 Do Begin
If (StrPos(AppState.States[ZeroPops[I]].State, 'LAKE') $
eq -1) then Begin
AppState.StateFaces[ZeroPops[I]]->SetProperty, $
Color = [20, 20, 20]
EndIf
If (AppState.PreviousScales[ZeroPops[I]] ne 1.e-4) then Begin
AppState.PreviousScales[ZeroPops[I]] = 1.e-4
AppState.StateObjects[ZeroPops[I]]->Scale, 1., 1., 1.e-4
EndIf
EndFor
;
; Determine the color to draw the State based on the population
; change from the previous decade.
;
If (AppState.CurrentYearIndex lt $
N_elements(AppState.States[0].Population) - 1) then Begin
Colors = USCensus$ZColors(AppState.States.Population[ $
AppState.CurrentYearIndex]/ $
Float(AppState.States.Population[ $
AppState.CurrentYearIndex + 1]), $
AppState.PopulationScale)
EndIf
;
; Label the census year.
;
AppState.TextObject->SetProperty, String = $
StrTrim(1980 - (AppState.CurrentYearIndex - 1)*10, 2)
;
; Loop over the States for which there are valid (non-zero) census
; data for this decade.
;
For I = 0, NOkayPops - 1 Do Begin
ThisState = OkayPops[I]
;
; Set the Z scale of the object so that the previous decade = 1 at
; the current height. This allows us to calculate a relative scale
; based on the population change.
;
AppState.StateObjects[ThisState]->GetProperty, $
Transform = Transform
AppState.StateObjects[ThisState]->Scale, 1., 1., $
1./Transform[2, 2]
;
; Scale the height of the State relative to the maximum population.
;
ZScale = AppState.States[ThisState].Population[ $
AppState.CurrentYearIndex]/ $
AppState.MaxZValue
AppState.StateObjects[ThisState]->Scale, 1., 1., ZScale
;
; Determine the color to make the State, based on relative population
; change from the previous decade.
;
If (StrPos(AppState.States[ThisState].State, 'LAKE') eq -1) $
then Begin
If (AppState.CurrentYearIndex eq $
N_elements(AppState.States[0].Population) - 1) then Begin
;
; If we're in the first year of the census, make the States white.
;
Color = [255, 255, 255]
EndIf Else Begin
If (AppState.States[ThisState].Population[ $
AppState.CurrentYearIndex + 1] eq 0) then Begin
;
; If the State did not have a census performed in the previous decade,
; color the State white.
;
Color = [255, 255, 255]
EndIf Else Begin
;
; We had census data from the previous decade, so color the State
; appropriately.
;
Color = Reform(Colors[*, ThisState])
EndElse
EndElse
AppState.StateSkirts[ThisState]->SetProperty, Color = Color
AppState.StateFaces[ThisState]->SetProperty, Color = Color
EndIf
;
; If we're on a fast machine, we might want to animate the States
; individually growing.
;
If (AppState.AnimateStates) then Begin
Dummy = Check_Math()
AppState.WindowObject->Draw, AppState.ViewObjects[0], $
Draw_Instance = AppState.Backdrop
EndIf
AppState.PreviousScales[OkayPops[I]] = ZScale
EndFor
;
; If we're not instancing the data, then just draw the view.
;
Dummy = Check_Math()
If (AppState.AnimateStates eq 0) then Begin
AppState.WindowObject->Draw, AppState.ViewObjects[0], $
Draw_Instance = AppState.Backdrop
EndIf
;
; Clean up any divide by zero, messages that may have
; accumulated.
;
Dummy = Check_Math()
End
;Pro USCensus$Animate_Census, AppState
;
; Loop over the census years.
;
;For J = N_elements(AppState.States[0].Population) - 1, 0, -1 $
; Do Begin
;
; We rotate the plane on which the States sit at each decade
; to enhance the relief in the early years of the census and
; to focus primarily on the east coast.
;
; AppState.ThetaY = -(J + 4) * 1.25
; AppState.CurrentYearIndex = J
; Widget_Control, AppState.ThetaYSlider, $
; Set_Value = AppState.ThetaY
; USCensus$Display_Census_Year, AppState
;EndFor
;End
Pro USCensus$Cleanup, AppState, TLB
;
; Handle the widget destruction through a File/Exit or
; system menu close.
;
Widget_Control, TLB, Get_UValue = AppState
Obj_Destroy, AppState.ViewObjects[0]
Obj_Destroy, AppState.ViewObjects[1]
Obj_Destroy, AppState.BackdropImage
Obj_Destroy, AppState.WindowObject
Obj_Destroy, AppState.Fonts[0]
Obj_Destroy, AppState.Fonts[1]
Widget_Control, TLB, /Destroy
;
; Free the pointers. Save the whales.
;
Ptr_Free, AppState.States.pOutline
Ptr_Free, AppState.States.pNormalOutline
Ptr_Free, AppState.States.pHighResOutline
Ptr_Free, AppState.States.pLowIndices
Ptr_Free, AppState.States.pPolygonList
Ptr_Free, AppState.States.pVertexList
End
Pro D_USCensus_Event, Event
;
; This routine is the main event handler for the widget.
;
Widget_Control, /Hourglass
If (Tag_Names(Event, /Structure_Name) eq 'WIDGET_KILL_REQUEST') $
then Begin
USCensus$Cleanup, AppState, Event.Top
!except = AppState.ExceptSave
Return
EndIf
If (Tag_Names(Event, /Structure_Name) eq 'WIDGET_BASE') then Begin
;
; Handle resize events on the top level base.
;
Widget_Control, /Hourglass
Widget_Control, Event.Top, Get_UValue = AppState
;
; Size the draw windows appropriately, given the new dimensions
; of the base.
;
ScaleGeom = Widget_Info(AppState.wScale, /Geometry)
ButtonBaseGeom = Widget_Info(AppState.wControls, /Geometry)
Widget_Control, AppState.wDraw, XSize = Event.X - $
ButtonBaseGeom.XSize, $
YSize = Event.Y - ScaleGeom.YSize
Widget_Control, AppState.wScale, XSize = Event.X - $
ButtonBaseGeom.XSize
Widget_Control, Event.ID, XSize = Event.X, YSize = Event.Y
;
; Resize the backdrop, if it's there, and create the
; new instance.
;
If (AppState.Backdrop) then Begin
AppState.ViewObjects[0]->SetProperty, Transparent = 0
AppState.StateSurfaceModel->SetProperty, Hide = 1
AppState.TextObject->SetProperty, Hide = 1
AppState.BackdropModel->SetProperty, Hide = 0
AppState.WindowObject->Draw, AppState.ViewObjects[0], $
/Create_Instance
AppState.StateSurfaceModel->SetProperty, Hide = 0
AppState.TextObject->SetProperty, Hide = 0
AppState.BackdropModel->SetProperty, Hide = 1
AppState.ViewObjects[0]->SetProperty, Transparent = 1
EndIf
;
; Draw the view.
;
AppState.WindowObject->Draw, AppState.ViewObjects[0], $
Draw_Instance = AppState.Backdrop
AppState.ScaleObject->Draw, AppState.ViewObjects[1]
Widget_Control, Event.Top, /Clear_Events
Return
EndIf
Widget_Control, Event.Top, Get_UValue = AppState
Widget_Control, Event.ID, Get_UValue = UValue
Case UValue of
'Quit' : Begin
USCensus$Cleanup, AppState, Event.Top
Return
End
'Draw' : Begin
;
; Left mouse button press events cause the selected
; State's name to be displayed by the text object.
;
Case Event.Type of
0 : Begin
If (Event.Press eq 1) then Begin
Widget_Control, /Hourglass
Picked = AppState.WindowObject->Select( $
AppState.ViewObjects[0], [Event.X, Event.Y])
If (N_elements(Picked) ne 0) then Begin
If (Obj_Valid(Picked[0])) then Begin
If (Obj_IsA(Picked[0], 'idlgrmodel')) $
then Begin
Picked[0]->IDLGrModel::GetProperty, $
Parent = ParentObject
EndIf Else Begin
Picked[0]->GetProperty, Parent = $
ParentObject
EndElse
ParentObject->GetProperty, UValue = StateName
;
; If the object is a State, put the State's name "on the glass".
; Redrawing the entire scene shouldn't be necessary but I don't
; know the technique for just getting the text object to change.
;
If (N_elements(StateName) ne 0) then Begin
StateName = StateName[0]
USState = Where(AppState.States.State $
eq StateName, NState)
If (NState ne 0) then Begin
AppState.TextObject->SetProperty, $
String = $
AppState.States[USState[0]].State
; AppState.ViewObjects[1]->SetProperty, Hide = 1
; AppState.WindowObject->Draw, AppState.ViewObjects[0], /Draw_Instance
; AppState.ViewObjects[1]->SetProperty, Hide = 0
Dummy = Check_Math()
AppState.WindowObject->Draw, $
AppState.ViewObjects[0]
EndIf
EndIf
EndIf
EndIf
EndIf
End
4 : Begin
;
; We got an EXPOSE event on one of the draw windows.
;
AppState.ScaleObject->Draw, AppState.ViewObjects[1]
AppState.WindowObject->Draw, AppState.ViewObjects[0]
Widget_Control, Widget_Info(Event.ID, /Parent), /Clear_Events
End
Else:
EndCase
End
'CancelAnimation' : Begin
Widget_Control, AppState.AnimateButton, Set_Value = 'Animate Census', $
Set_UValue = 'Animate'
Widget_Control, Event.Top, /Clear_Events
End
'DoingAnimation' : Begin
;
; This event is thrown from the controls base. We use a timer event off
; this base to indicate "frame advance" in the animation.
;
If (AppState.CurrentYearIndex ne 0) then Begin
;
; Decrement the year counter (move forward in time), move the slider
; rotation angle widget, and display the data.
;
AppState.CurrentYearIndex = AppState.CurrentYearIndex - 1
AppState.ThetaY = -(AppState.CurrentYearIndex + 4) * 1.25
Widget_Control, AppState.ThetaYSlider, $
Set_Value = AppState.ThetaY
USCensus$Display_Census_Year, AppState
;
; Throw a timer event from the controls base. This will put us right
; back here into this case switch.
;
Widget_Control, AppState.wControls, Timer = .001
EndIf Else Begin
;
; We've reached the end of the animation, 1990. Reset the "Animate Census"
; button back to its original state, and clear the events.
;
Widget_Control, AppState.AnimateButton, Set_Value = 'Animate Census', $
Set_UValue = 'Animate'
Widget_Control, Event.Top, /Clear_Events
EndElse
End
'Animate' : Begin
;
; Modify the "Animate Census" Button so it will now throw "CancelAnimation"
; events.
;
AppState.StateSurfaceModel->SetProperty, $
Transform = AppState.InitialTransform
Widget_Control, AppState.AnimateButton, $
Set_Value = 'Cancel Animation', $
Set_UValue = 'CancelAnimation'
;
; Set the year index to point to 1790.
;
AppState.CurrentYearIndex = N_elements(AppState.States[0].Population); - 1
;
; Toss a timer event from the controls base. Events from the controls
; base are interpreted as being "animation frame advance" events.
;
Widget_Control, AppState.wControls, Timer = .001
End
'Legend' : Begin
AppState.ScaleObject->Draw, AppState.ViewObjects[1]
AppState.WindowObject->Draw, AppState.ViewObjects[0]
End
'CensusYear' : Begin
AppState.CurrentYearIndex = $
N_elements(AppState.States[0].Population) - Event.Value
USCensus$Display_Census_Year, AppState
Widget_Control, Event.ID, /Clear_Events
End
'YRotation' : Begin
AppState.ThetaY = Event.Value
;
; If individual state animation was in effect, turn it
; off for the rotation since it serves a purpose only
; in year changes.
;
AnimateStates = AppState.AnimateStates
AppState.AnimateStates = 0
USCensus$Display_Census_Year, AppState
AppState.AnimateStates = AnimateStates
Widget_Control, Event.ID, /Clear_Events
End
'HelpCensusData' : Begin
If (not XRegistered('About Census Data')) then Begin
TextTLB = Widget_Base(Group_Leader = Event.Top, /Column)
Widget_Control, TextTLB, TLB_Set_Title = $
'About the Census Data'
T = Widget_Text(TextTLB, XSize = 60, YSize = 15, $
Value = AppState.DataText, /Scroll)
OkayButton = Widget_Button(TextTLB, Value = 'OK', $
UValue = 'KillAboutCensus')
Widget_Control, TextTLB, /Realize
XManager, 'About Census Data', TextTLB, Event_Handler = $
'D_USCensus_Event'
EndIf
End
'HelpCensusDemo' : Begin
If (not XRegistered('About Census Demo')) then Begin
TextTLB = Widget_Base(Group_Leader = Event.Top, /Column)
Widget_Control, TextTLB, TLB_Set_Title = $
'About the Census Demo'
T = Widget_Text(TextTLB, XSize = 60, YSize = 15, $
Value = AppState.DemoText, /Scroll)
OkayButton = Widget_Button(TextTLB, Value = 'OK', $
UValue = 'KillAboutCensus')
Widget_Control, TextTLB, /Realize
XManager, 'About Census Demo', TextTLB, Event_Handler = $
'D_USCensus_Event'
EndIf
End
'KillAboutCensus' : Begin
Widget_Control, Event.Top, /Destroy
Return
End
Else :
EndCase
Widget_Control, Event.Top, Set_UValue = AppState
End
;-----------------------------------------------------------------
;
; PURPOSE : cleanup procedure. restore colortable.
;
pro USCensus_Cleanup, wTopBase
WIDGET_CONTROL, wTopBase, GET_UVALUE=sState, /NO_COPY
; Restore the color table.
;
TVLCT, sState.colorTable
if widget_info(sState.groupBase, /valid) then $
widget_control, sState.groupBase, /map
end ; of USCensus_Cleanup
Pro D_Uscensus, XSize = XSize, YSize = YSize, Axes = Axes, $
Animate_States = Animate_States, Backdrop = Backdrop, $
New_England = New_England, Depth = Depth, $
Group_Leader = Group_Leader, AppTLB = AppTLB
;+
; FILE:
; D_Uscensus.pro
;
; CALLING SEQUENCE: D_Uscensus [, GROUP_LEADER = GROUP_LEADER $]
; [, XSIZE = XSIZE $]
; [, YSIZE = YSIZE $]
; [, /AXES $]
; [, /ANIMATE_STATES $]
; [, /NEW_ENGLAND $]
; [, /BACKDROP $]
; [, /DEPTH]
;
; PURPOSE:
; This application also illustrates handling draw widget base
; resizing and exposure events in the context of Object
; Graphics, pointer and object heap variable management and
; cleanup, and, when /BACKDROP is enabled, instancing of IDLgrView
; objects and image texture mapping to an IDLgrSurface object.
;
; The RGB Object Graphics color model is used exclusively in this
; demo; None of the code uses Color Index (CI) model.
;
; The procedure was authored by the Professional Services Group
; of Research Systems. For those desiring a larger
; implementation, we can be contacted (psg@rsinc.com) for
; consulting and custom coding.
;
; KEYWORD PARAMETERS:
; GROUP_LEADER can be set to the ID of a parent widget when
; this routine is called as a compound widget.
;
; APPTLB returns the application top level base, mainly for
; use in the IDL Demo (i.e., don't worry about this if you
; aren't RSI.)
;
; XSIZE, YSIZE can be used to define the size of the main
; draw window. By default, the values are 467 and 376 pixels,
; respectively.
;
; /AXES specifies that lines showing the X, Y, and Z
; axes should be rendered. By default these are not shown.
;
; /ANIMATE_STATES when set allows States to be individually
; rendered when a new year of data is displayed. By default
; all States are scaled before the view is rendered. This
; option is recommended only on machines with good rendering
; speed, such as a Pentium Pro 200 or Sun Ultra.
;
; /BACKDROP when set causes a backdrop image to be drawn behind
; the USA map. It's a good example of view object instancing.
; This is recommended only for machines with TrueColor displays
; and good rendering speed. By default, the backdrop image is
; not displayed.
;
; /DEPTH turns on depth cueing in the view. By default, depth
; cueing is not performed.
;
; RETURN VALUE:
; MAJOR TOPICS: Visualization, Analysis, Demo, Language
;
; CATEGORY:
; IDL 5.0
;
; INTERNAL FUNCTIONS and PROCEDURES:
; fun USCensus$ZCOLORS - Determine color to shade State
; objects based on population
; gain or loss.
; pro USCensus$NEW_COORDINATES
; - Turn latitutde/longitudes for
; State outlines into device
; coordinates, and build the
; "skirts".
; pro USCensus$BUILD_BACKDROP
; - Build the backdrop "drapery"
; objects
; pro USCensus$BUILD_SCALE_LEGEND
; - Build the legend for the display
; pro USCensus$BUILD_STATE_OBJECTS
; - Build the individual State
; objects
; pro USCensus$DISPLAY_CENSUS_YEAR
; - Draw the scene for the currently
; selected year
; pro USCensus$ANIMATE_CENSUS
; - Animate the census data from 1790
; to 1990
; pro USCensus$CLEANUP - Clean up objects and heap
; variables when the application
; is completed.
; pro USCensus_EVENT - The main event handler
;
; EXTERNAL FUNCTIONS, PROCEDURES, and FILES:
; states.sav - IDL SAVE file containing State
; outlines and Census data.
;
; REFERENCE: IDL Reference Guide, IDL User's Guide
;
; NAMED STRUCTURES:
; none.
;
; COMMON BLOCKS:
; none.
;
; SYSTEM VARIABLES:
; none.
;
; MODIFICATION HISTORY:
; 3/97, JLP - Completed version for IDL 5.0.
; 4/11/97, JLP - Restricted rotation angle selection
; Added ability to stop animation
; Modified viewport size
; Added check_math() traps for text objects
;-
;--------------------------------------------------------------------
If (XRegistered('D_Uscensus', /NoShow)) then Begin
v = Dialog_Message('An instance of D_Uscensus is already running.')
Return
EndIf
If (N_elements(Group_Leader) ne 0) then Begin
If (N_elements(Group_Leader) eq 1) then Begin
OkayGL = Widget_Info(Group_Leader, /Valid_ID)
EndIf Else Begin
OkayGL = 0
EndElse
If (not OkayGL) then Begin
v = Dialog_Message('The GROUP_LEADER parameter is invalid.')
Return
EndIf
groupBase = Group_Leader
EndIf else groupBase = 0L
ngroup = N_elements(Group_Leader)
; ; Create the starting up message.
; ;
; if (ngroup EQ 0) then begin
; drawbase = startmes()
; endif else begin
; drawbase = startmes(GROUP=group)
; endelse
; Get the current color vectors to restore
; when this application is exited.
;
TVLCT, savedR, savedG, savedB, /GET
; Build color table from color vectors
;
colorTable = [[savedR],[savedG],[savedB]]
; Get the tips.
;
; sText = getTips('uscensus.tip')
sText = getTips(filepath('uscensus.tip', $
SUBDIR=['examples','demo', 'demotext']) )
;
; On slow platforms, we only use New England.
;
NewEnglandStates = ['MAINE', 'NEW HAMPSHIRE', 'VERMONT', $
'MASSACHUSETTS', 'CONNECTICUT', 'RHODE ISLAND']
NewEngland = Keyword_Set(New_England)
If (Keyword_Set(Depth)) then Begin
Depth_Cue = [-1.5, 1.]
EndIf Else Begin
Depth_Cue = [0., 0.]
EndElse
;
; We show relative population changes from decade to decade
; via color indices.
;
RangeMax = [1., 1.01, 1.1, 1.2, 1.5, 2., 3.]
Colors = [ $
[255, 0, 0], $
[115, 40, 100], $
[105, 95, 105], $
[15, 150, 0], $
[0, 255, 0], $
[105, 105, 200], $
[145, 195, 125], $
[195, 215, 255]]
PopulationScale = {RangeMax : RangeMax, Colors : Colors}
;
; Set the draw window size.
;
DEVICE, GET_SCREEN_SIZE = screenSize
XDim= 0.55 * screenSize(0)
YDim= 0.8 * XDim
;XDim = 467
;YDim = 376
If (N_elements(XSize) eq 1) then Begin
XDim = XSize[0] > 0
EndIf
If (N_elements(YSize) eq 1) then Begin
YDim = YSize[0] > 0
EndIf
;
; Restore the States database. This contains
; the State outlines and census data.
;
F = FindFile('states.sav', Count = Count)
If (Count eq 0) then Begin
Restore, FilePath('states.sav', SubDir = $
['examples', 'demo', 'demodata'])
EndIf Else Begin
Restore, 'states.sav'
EndElse
NStates = N_elements(States)
NYears = N_elements(States[0].Population)
For I = 0, NStates - 1 Do Begin
If (Ptr_Valid(States[I].pLowIndices)) then Begin
States[I].pOutline = Ptr_New((*States[I].pHighResOutline) $
[*, *States[I].pLowIndices])
EndIf Else Begin
States[I].pOutline = States[I].pHighResOutline
EndElse
EndFor
;
; Calculate State outlines and extruded skirts in normalized
; coordinates based on the current window size.
;
USCensus$New_Coordinates, States, XDim, YDim
;
; Create widgets.
;
If (Keyword_Set(Group_Leader)) then Begin
wBase = Widget_Base(/COLUMN, Group=Group_Leader, MBar=MenuBar, $
XPad = 0, YPad = 0, Title = 'US Census Data', $
/TLB_Kill_Request_Events, /TLB_Size_Events, Space = 0)
EndIf Else Begin
wBase = Widget_Base(/COLUMN, MBar = MenuBar, $
XPad = 0, YPad = 0, Title = 'US Census Data', $
/TLB_Kill_Request_Events, /TLB_Size_Events, Space = 0)
EndElse
wBase1 = WIDGET_BASE(wBase,/Row)
AppTLB = wBase
FileMenu = Widget_Button(MenuBar, Value = 'File', /Menu)
QuitButton = Widget_Button(FileMenu, Value = 'Quit', /Separator, $
UValue = 'Quit')
HelpMenu = Widget_Button(MenuBar, Value = 'About', /Help, /Menu)
HelpButton = Widget_Button(HelpMenu, Value = 'About the data...', $
UValue = 'HelpCensusData')
HelpButton = Widget_Button(HelpMenu, Value = 'About the demo...', $
UValue = 'HelpCensusDemo')
DataText = [ $
'', $
' This demonstration illustrates United States Census Bureau ', $
' data collected in the decades 1790-1990.', $
'', $
' State heights represent total population, while colors', $
' indicate relative population change from the preceding', $
" decade's census.", $
'', $
' States for which no data were collected in the previous', $
' decade are displayed in white.']
DemoText = [ $
'', $
' The "Animate Census" button will cycle through the U.S.', $
' Census data from 1790 to 1990. At each decade, the field', $
' of view is rotated slightly about the Y-axis, roughly', $
' bisecting the map, in order to provide a changing perspective.', $
' ', $
' The "Rotation Angle" slider widget can be used to explicitly', $
' define an angle about the Y-axis at which to view the data.', $
'', $
" Clicking on a State will cause the State's name to be", $
" displayed, illustrating Object Graphics' capabilites for", $
' interactively selecting 3D objects.', $
'', $
' The "Select Census Year" pull-down menu lets you select', $
' a specific year of data to be shown.', $
'', $
" The individual State outlines were derived from IDL's map", $
' database.', $
'', $
' This procedure was authored by the Professional Services', $
' Group of Research Systems. For those desiring a larger', $
' implementation, the Professional Services Group can be', $
' contacted (psg@rsinc.com) for consulting and custom coding.']
wControls = Widget_Base(wBase1, XPad = 1, YPad = 1, Space = 1, $
/Column, Frame = 1, UValue = 'DoingAnimation')
AnimateButton = Widget_Button(wControls, Value = 'Animate Census', $
UValue = 'Animate')
MenuItem = {CW_PDMENU_S, Flags : 0, Name : ''}
CensusMenuItems = Replicate(MenuItem, NYears + 1)
CensusMenuItems.Flags = [1, IntArr(NYears - 1), 2]
CensusMenuItems.Name = ['Select Census Year', $
StrTrim(1790 + Indgen(NYears)*10, 2)]
CensusMenu = CW_PDMenu(wControls, CensusMenuItems, $
UValue = 'CensusYear', /Return_Index)
ThetaYSlider = Widget_Slider(wControls, Maximum = 40, Minimum = -40, $
Value =0, UValue = 'YRotation', Title = 'Rotation Angle')
Drawbase = Widget_Base(wBase1, /Column, XPad = 0, YPad = 0, Space = 0, Frame = 0)
wDraw = Widget_Draw(DrawBase, XSize = XDim, YSize = YDim, $
/Button_Events, UValue = 'Draw', Retain = 0, /Expose_Events, $
Graphics_Level = 2)
wScale = Widget_Draw(DrawBase, XSize = XDim, YSize = 60, $
UValue = 'Legend', Retain = 0, /Expose_Events, $
Graphics_Level = 2)
; Create the status line label.
;
wStatusBase = WIDGET_BASE(wBase, MAP=0, /ROW)
nWidgets = 2
wText = LONARR(nWidgets)
widTips, wStatusBase, sText.text, XSIZE=36, $
YSIZE=3, NWIDGETS=nWidgets, wText
;
; Realize the base widget.
;
Widget_Control, wBase, /Realize
; Size the tips widgets.
;
sizeTips, wBase, wText, wStatusBase
Widget_Control, /Hourglass
;
; Get the window objects associated with the two
; draw widgets we've created.
;
Widget_Control, wDraw, Get_Value = WindowObject
Widget_Control, wScale, Get_Value = ScaleObject
;
; Create the views. One will contain the USA and backdrop and
; the other will contain the scale legend.
;
ViewObject1 = Obj_New('IDLgrView', Projection = 1, $
Viewplane_Rect=[-.2, -.11, .4, .22], Color = [255, 255, 255], $
ZClip = [2., -1.], Depth_Cue = Depth_Cue)
ViewObject2 = Obj_New('IDLgrView', Projection = 1, $
Viewplane_Rect=[-1, -1, 2, 2], Color = [0, 0, 0])
;
; Build the backdrop
;
BackdropModel = Obj_New('IDLgrModel')
USCensus$Build_Backdrop, BackdropImage, BackdropObject
BackdropModel->Add, BackdropObject
If (not Keyword_Set(Backdrop)) then Begin
BackdropObject->SetProperty, Hide = 1
EndIf
;
; Create fonts for labels.
;
Fonts = ObjArr(2)
Fonts[0] = Obj_New('IDLgrFont', 'Times*Bold', Size = 14 - $
2*(!d.y_ch_size gt 12))
Fonts[1] = Obj_New('IDLgrFont', 'Times', Size = 12 - $
2*(!d.y_ch_size gt 12))
;
; Create the population scale legend, add it to the
; appropriate view, and draw it.
;
USCensus$Build_Scale_Legend, PopulationScale, Fonts[1], ScaleModel
ViewObject2->Add, ScaleModel
ScaleObject->Draw, ViewObject2
;
; Create a model for lights and place an ambient and
; directional light into the appropriate view.
;
LightFrame = Obj_New('IDLgrModel')
Light1 = Obj_New('IDLgrLight', Type = 0, Intensity = 0.85, $
Color = [255, 255, 255])
Light2 = Obj_New('IDLgrLight', Location = [0, 2, 2], Type = 1, $
Color = [255, 255, 255], Intensity = .5)
LightFrame->Add, Light1
LightFrame->Add, Light2
ViewObject1->Add, LightFrame
USCensus$Build_State_Objects, States, StateSurfaceModel, $
StateObjects, StateSkirts, StateFaces, StateOutlines
;
; If we're only looking at New England, hide the other States.
;
If (NewEngland) then Begin
For I = 0, NStates - 1 Do Begin
IsNewEngland = Where(NewEnglandStates eq States[I].State, $
NIsNewEngland)
If (NIsNewEngland eq 0) then Begin
StateObjects[I]->SetProperty, Hide = 1
EndIf
EndFor
EndIf
;
; Sort of center the USA (or New England) on the origin in the
; X-Y plane.
;
;StateSurfaceModel->Translate, -.46 - NewEngland*.13, $
; -.24 - NewEngland*.041, 0.
StateSurfaceModel->Translate, -.44 - NewEngland*.13, $
-.24 - NewEngland*.041, 0.
;
; Scale up New England if we're not looking at the entire USA.
;
If (NewEngland) then Begin
StateSurfaceModel->Scale, 4., 4., 1.
EndIf
;
; Rotate the States 30 degrees about the Y axis. This lets us
; look at the Z values from an oblique angle.
;
ThetaY = -30 ;*(1. - (Keyword_Set(Depth)*.75))
StateSurfaceModel->Rotate, [0., 1., 0.], ThetaY
;
; Translate the model up Z toward the viewer so we can
; make room for the backdrop.
;
StateSurfaceModel->Translate, 0., 0., .5
;
; Create crosshairs at the origin, if requested.
;
If (Keyword_Set(Axes)) then Begin
AxisObject = Obj_New('IDLgrModel')
Xaxis = Obj_New('IDLgrPolyline', [[-.2, 0., 0.], [.2, 0., 0.]], $
Color=[0, 0, 0], Thick=1.2)
Yaxis = Obj_New('IDLgrPolyline', [[0., -.2, 0.], [0., .2, 0.]], $
Color=[0, 0, 0], Thick=1.2)
Zaxis = Obj_New('IDLgrPolyline', [[0., 0., -.2], [0., 0., .2]], $
Color=[0, 0, 0], Thick=1.2)
AxisObject->Add, XAxis
AxisObject->Add, YAxis
AxisObject->Add, ZAxis
ViewObject1->Add, AxisObject
EndIf
;
; Add the State surface to the appropriate view.
;
ViewObject1->Add, StateSurfaceModel
;
; Save the initial transformation matrix. This allows
; us to return to this initial position if we want
; to re-animate the data.
;
StateSurfaceModel->GetProperty, Transform = InitialTransform
;
; If we're employing a backdrop image, we only need to draw it
; once as an instance and use that in subsequent draws of the USA.
;
If (Keyword_Set(Backdrop)) then Begin
ViewObject1->Add, BackdropModel
StateSurfaceModel->SetProperty, Hide = 1
BackdropModel->SetProperty, Hide = 0
WindowObject->Draw, ViewObject1, /Create_Instance
StateSurfaceModel->SetProperty, Hide = 0
BackdropModel->SetProperty, Hide = 1
ViewObject1->SetProperty, Transparent = 1
EndIf
;
; Add a text object which will hold the census year. It
; also holds the name of the State when the user clicks.
;
TextModel = Obj_New('IDLgrModel')
If (Keyword_Set(Backdrop)) then Begin
TextColor = [255, 255, 155]
Endif Else Begin
TextColor = [0, 0, 0]
EndElse
TextObject = Obj_New('IDLgrText', '', Location = [.13, -.08], $
Color = TextColor, /OnGlass, Font = Fonts[0], Alignment = .5)
TextModel->Add, TextObject
ViewObject1->Add, TextModel
;
; Save the application state.
;
ExceptSave = !except
!except = 0
AppState = { $
ViewObjects : [ViewObject1, ViewObject2], $
WindowObject : WindowObject, $
ScaleObject : ScaleObject, $
wDraw : wDraw, $
wScale : wScale, $
wControls : wControls, $
TextObject : TextObject, $
States : States, $
StateObjects : StateObjects, $
StateOutlines : StateOutlines, $
StateSkirts : StateSkirts, $
StateFaces : StateFaces, $
StateSurfaceModel : StateSurfaceModel, $
ThetaY : ThetaY, $
ThetaYSlider : ThetaYSlider, $
CurrentYearIndex: NYears - 1, $
Fonts : Fonts, $
BackdropModel : BackdropModel, $
BackdropImage : BackdropImage, $
DepthCue : Depth_Cue, $
AnimateStates : Keyword_Set(Animate_States), $
Backdrop : Keyword_Set(Backdrop), $
PreviousScales : FltArr(N_elements(States)) + 1., $
MaxZValue : Max(States.Population) * 1.2, $
PopulationScale : PopulationScale, $
InitialTransform: InitialTransform, $
DemoText : DemoText, $
DataText : DataText, $
AnimateButton : AnimateButton, $
ExceptSave : ExceptSave, $
ColorTable: colorTable, $ ; Color table to restore at exit
groupBase: groupBase $ ; Base of Group Leader
}
Widget_Control, ThetaYSlider, Set_Value = ThetaY
Widget_Control, wBase, Set_UValue = AppState
;Widget_Control, wBase, /Clear_Events
; ; Destroy the starting up window.
;; ;
; WIDGET_CONTROL, drawbase, /DESTROY
;
; ; Map the top level base.
; ;
; WIDGET_CONTROL, wBase, MAP=1
;
;Widget_Control, wBase, /HOURGLASS
USCensus$Display_Census_Year, AppState
;
; If we're only showing New England, we want to be a little more
; fancy; make New England act like a pendulum.
;
;If (NewEngland and not (Keyword_set(Depth))) then Begin
; AppState.WindowObject->SetProperty, QUALITY = (NewEngland eq 1)
; For I = 30, 394, 4 Do Begin
; Direction = (I mod 180 lt 90) - Long((I mod 180 ge 90))
;
; Translate the State back to the XY plane, rotate it about Y and Z
; then translate it back above the XY plane.
;
; StateSurfaceModel->Translate, 0., 0., -.5
; StateSurfaceModel->Rotate, [0., 1., 0.], 4.*Direction
; StateSurfaceModel->Rotate, [0., 0., 1.], 4.*Direction
; StateSurfaceModel->Translate, 0., 0., .5
; AppState.WindowObject->Draw, AppState.ViewObjects[0], $
; Draw_Instance = Keyword_Set(Backdrop)
; EndFor
;EndIf
;Widget_Control, wBase, /Clear_Events
XManager, 'D_Uscensus', wBase, $
/No_Block, $
CLEANUP='USCensus_Cleanup'
Return
End